// Copyright 2011 Google Inc. All Rights Reserved.

/**
 * @fileoverview This module provides a way to measure throughput via HTTP.
 *
 * @author gavaletz@google.com (Eric Gavaletz)
 *
 * This test operates by downloading large images over the same connection many
 * times.
 *
 * Large images on yt servers they seem to still hit
 * the cache, but at least the images are all spread
 * across the 4 servers (or i.ytimg.com??).
 *
 * http://i4.ytimg.com/i/w4eERuAeJm-ZyAtARhlsgw/1.jpg
 * http://i3.ytimg.com/i/2BwWKbFrVhAWjUQR0MgsaA/1.jpg
 * http://i4.ytimg.com/i/kvdPjkBrFHznMMRrFnm7nw/1.jpg
 * http://i2.ytimg.com/i/13BLlOyjAotz6cule0CBuw/1.jpg
 * http://i2.ytimg.com/i/MCRLUOecRYMkdvxzJRlJqQ/1.jpg
 * http://i1.ytimg.com/i/8dqXsvsGxKnyyxZssqjsUg/1.jpg
 * http://i1.ytimg.com/i/pF_Dv5_lbIaAimjzSqUzWA/1.jpg
 * http://i1.ytimg.com/i/XKHrmb4jWYAEMJen4sStLQ/1.jpg
 * http://i4.ytimg.com/i/3MRRyH3eyX7uRHSiOz_U3Q/1.jpg
 * http://i2.ytimg.com/i/EaBXwkn-SkotfgJmrN8vWg/1.jpg
 * http://i2.ytimg.com/i/-17pZVK8KkVHlbHZ_c278A/1.jpg
 * http://i4.ytimg.com/i/gR-9z_m3NKARlGVBX_K6QA/1.jpg
 * http://i3.ytimg.com/i/jxDRE3As6Fk0zKKqK_PNIQ/1.jpg
 *
 * Suggestion from youtube -- use the thumbnails and append _HQ to get a larger
 * file.
 */


goog.provide('thr');


goog.require('datastore');
goog.require('goog.array');
goog.require('late');
goog.require('page');


/**
 * The name for this test to be used when displaying results.
 *
 * @type {string}
 * @const
 */
thr.testName = 'thoughput';


/**
 * The server part of the url for throughput test image files.
 * @type {string}
 */
thr.server = 'i1.ytimg.com';


/**
 * Sets the server name/ip.  Usually just used from the server.
 *
 * @param {string} s the path to be used.
 */
thr.setServer = function(s) {
  thr.server = s;
};
//used by the closure compiler to eport this function call
goog.exportProperty(window, 'thrSetServer', thr.setServer);


/**
 * The resource part of the url for throughput test image files.
 *
 * For now the size from huge.jpg is 242554 Bytes.
 *
 * @type {string}
 */
thr.resource = '/i/pF_Dv5_lbIaAimjzSqUzWA/1.jpg';


/**
 * Sets the resource (image) path.  Usually just used from the server.
 *
 * @param {string} s the path to be used.
 */
thr.setResource = function(s) {
  thr.resource = s;
};
//used by the closure compiler to eport this function call
goog.exportProperty(window, 'thrSetResource', thr.setResource);


/**
 * The size of the file to be loaded to test throughput.
 *
 * @type {number}
 */
thr.objSize = 1940.432;  //242554 * 8 / 1000.0


/**
 * Sets the object size.  Usually just used from the server.
 *
 * @param {number} n the size of the object in bytes.
 */
thr.setObjSize = function(n) {
  thr.objSize = n;
};
//used by the closure compiler to eport this function call
goog.exportProperty(window, 'thrSetObjSize', thr.setObjSize);


/**
 * The limit for the number of files to be loaded.
 *
 * @type {number}
 */
thr.maxPings = 100;


/**
 * Sets the maximum number of pings.  Usually just used from the server.
 *
 * @param {number} n the maximum number of files to load.
 */
thr.setMaxPings = function(n) {
  thr.maxPings = n;
};
//used by the closure compiler to eport this function call
goog.exportProperty(window, 'thrSetMaxPings', thr.setMaxPings);


/**
 * The maximum amount of time that this test should be allowed to run.
 *
 * @type {number}
 */
thr.timeLimit = 1000.0;


/**
 * Sets the time limit.  Usually just used from the server.
 *
 * @param {number} n the maximum time in seconds.
 */
thr.setTimeLimit = function(n) {
  thr.timeLimit = n;
};
//used by the closure compiler to eport this function call
goog.exportProperty(window, 'thrSetTimeLimit', thr.setTimeLimit);


/**
 * The limit (lower) for the number of files to be loaded.
 *
 * @type {number}
 */
thr.minPings = 10;


/**
 * Sets the minimum number of pings.  Usually just used from the server.
 *
 * @param {number} n the minimum number of files to load.
 */
thr.setMinPings = function(n) {
  thr.minPings = n;
};
//used by the closure compiler to eport this function call
goog.exportProperty(window, 'thrSetMinPings', thr.setMinPings);


/**
 * The median response time.
 *
 * @type {number}
 */
thr.medResponseTime;


/**
 * The median transfer time.
 *
 * @type {number}
 */
thr.medTransferTime;


/**
 * The minimum response time.
 *
 * @type {number}
 */
thr.minTransferTime;


/**
 * The median goodput.
 *
 * @type {number}
 */
thr.goodput;


/**
 * The maximum goodput.
 *
 * @type {number}
 */
thr.maxGoodput;


/**
 * The next thing to do.
 *
 * @type {function()}
 */
thr.nextFunction;


/**
 * Sets up the netscore state to recieve updates and loads the first image.
 */
thr.start = function() {
  if (late.minRtt == undefined) {
    datastore.logError('thrError', 'lateMinRtt is undefined');
    if (thr.nextFunction) {
      thr.nextFunction();
    }
  }
  datastore.json['thrServer'] = thr.server;
  datastore.json['thrResource'] = thr.resource;
  datastore.json['thrObjSize'] = thr.objSize;
  page.progressSetTestName(thr.testName);
  page.progressSetTestMessage(nsi18n('thrProgress'));
  resptime.setup(thr.server, thr.resource, thr.maxPings, thr.minPings,
      thr.timeLimit, thr.finish, page.progressSetProgressBar);
  resptime.start();
};


/**
 * Calculates an average throughput and updates the page with that result.
 *
 * @param {Array.<number>} data the list of response times.
 */
thr.finish = function(data) {
  datastore.json['thrDelay'] = goog.array.clone(data);
  goog.array.sort(data);
  thr.medResponseTime = data[Math.round(data.length / 2)];
  thr.medTransferTime = thr.medResponseTime - late.minRtt;
  thr.minTransferTime = data[0] - late.minRtt;
  thr.goodput = thr.objSize / thr.medTransferTime;
  thr.maxGoodput = thr.objSize / thr.minTransferTime;
  datastore.json['thrMedTransferTime'] = thr.medTransferTime;
  datastore.json['thrMinTransferTime'] = thr.minTransferTime;
  datastore.json['thrMedResponseTime'] = thr.medResponseTime;
  datastore.json['thrGoodput'] = thr.goodput;
  datastore.json['thrMaxGoodput'] = thr.maxGoodput;
  page.progressClear();
  if (thr.nextFunction) {
    thr.nextFunction();
  }
};


/**
 * Displays results based on the data stored in the datastore.
 */
thr.results = function() {
  var thrDisplayDiv = goog.dom.createDom('div', {'class': 'displayChart'});
  var thrDescripDiv = goog.dom.createDom('div', {'class': 'message'},
      nsi18n('thrDescrip1'));
  var rtDisplayDiv = goog.dom.createDom('div', {'class': 'displayChart'});
  var rtDescripDiv = goog.dom.createDom('div', {'class': 'message'},
      nsi18n('thrDescrip2'));
  var otherDescripDiv = goog.dom.createDom('div', {'class': 'message'},
      nsi18n('thrDescrip3'));
  var otherTableDisplayDiv = goog.dom.createDom('div', {'class': 'table'});
  var contentDiv = goog.dom.createDom('div', {}, thrDescripDiv, thrDisplayDiv,
      rtDescripDiv, rtDisplayDiv, otherDescripDiv, otherTableDisplayDiv);
  page.resultsAppend(thr.testName, contentDiv);

  var goodput = [];
  for (i = 0; i < datastore.json['thrDelay'].length; i++) {
    goodput[i] = datastore.json['thrObjSize'] / (datastore.json['thrDelay'][i] -
        datastore.json['lateMinRtt']);
  }

  // results will generate the counts for us.
  var thrDataObj = results.dataToObj(null, goodput, nsi18n('Object Count'),
      nsi18n('Goodput (Mbps)'), 'number', 'number');
  results.scatterChart(thrDataObj, nsi18n('Goodput'), thrDisplayDiv);

  // results will generate the counts for us.
  var dataObj = results.dataToObj(null, datastore.json['thrDelay'],
      nsi18n('Object Count'), nsi18n('Response Time (ms)'), 'number', 'number');
  results.scatterChart(dataObj, nsi18n('Response Times'), rtDisplayDiv);

  var otherLables = [nsi18n('Server'), nsi18n('Resource'),
        nsi18n('Median Response Time (ms)'),
        nsi18n('Median Transfer Time (ms)'),
        nsi18n('Minimum Transfer Time (ms)'),
        nsi18n('Median Goodput (Mbps)'), nsi18n('Maximum Goodput (Mbps)')];
  var otherValues = [datastore.json['thrServer'], datastore.json['thrResource'],
    datastore.json['thrMedResponseTime'],
    datastore.json['thrMedTransferTime'],
    datastore.json['thrMinTransferTime'], datastore.json['thrGoodput'],
    datastore.json['thrMaxGoodput']];
  var otherDataObj = results.dataToObj(otherLables, otherValues,
      nsi18n('Field'), nsi18n('Value'), 'string', 'number');
  results.table(otherDataObj, otherTableDisplayDiv);
};
